/*
 * TransparencyImpl.java    0.0.1    23/dic/2009
 * 
 * Copyright (c) 2009 mentalsmash.org
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * The use of the Apache License does not indicate that this project is
 * affiliated with the Apache Software Foundation.
 */ 
package org.mentalsmash.tazio.transparency.impl;

import java.io.IOException;
import java.io.Serializable;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;

import org.mentalsmash.tazio.commons.TazioRuntimeException;
import org.mentalsmash.tazio.commons.identifiers.NID;
import org.mentalsmash.tazio.net.cs.CSSubsystemException;
import org.mentalsmash.tazio.net.cs.server.CSComFacade;
import org.mentalsmash.tazio.net.cs.server.Configuration;
import org.mentalsmash.tazio.transparency.commons.messages.TazioMessage;
import org.mentalsmash.tazio.transparency.interfaces.Deliverer;
import org.mentalsmash.tazio.transparency.interfaces.Dispatcher;
import org.mentalsmash.tazio.transparency.interfaces.InputMessageListener;
import org.mentalsmash.tazio.transparency.interfaces.MessageFactory;
import org.mentalsmash.tazio.transparency.interfaces.MessageFilter;
import org.mentalsmash.tazio.transparency.interfaces.Resolver;
import org.mentalsmash.tazio.transparency.interfaces.Transparency;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.inject.Inject;
import com.google.inject.name.Named;

/**
 * This is the base implementation of the {@link Transparency} interface.
 * 
 * @version 0.0.1	23/dic/2009
 * @author Enrico Savioli <savioli.enrico@gmail.com>
 */
public class TransparencyImpl implements Transparency{

	private final static Logger log = LoggerFactory.getLogger(TransparencyImpl.class);
	
	private final String CONFIGURATION_FILE;
	private final int MAX_SENDING_REPETITIONS;
	private final Resolver resolver;
	private final MessageFactory messageFactory;
	private final Dispatcher dispatcher;
	// private final MessageFilter messageFilter;
	
	private CSComFacade facade = null;
	
	@Inject
	public TransparencyImpl(MessageFactory messageFactory,
			Resolver resolver,
			Dispatcher dispatcher,
			MessageFilter messageFilter,
			@Named("max_sending_repetitions")int MAX_SENDING_REPETITIONS,
			@Named("facade_conf_file")String CONFIGURATION_FILE){
		
		this.CONFIGURATION_FILE = CONFIGURATION_FILE;
		this.MAX_SENDING_REPETITIONS = MAX_SENDING_REPETITIONS;
		this.messageFactory = messageFactory;
		this.resolver = resolver;
		this.dispatcher = dispatcher;
		// this.messageFilter = messageFilter;
		
	}
	
	/* (non-Javadoc)
	 * @see org.mentalsmash.tazio.transparency.Transparency#connect()
	 */
	@Override
	public void connect() {
		
		try{
			Configuration conf = Configuration.fromFile(this.CONFIGURATION_FILE);
			log.debug("Creating WRM cs facade and making it listen on " + conf.address + ":" + conf.portNumber);
			facade = new CSComFacade(conf,
					ProtocolHandler.class);
			
			
			
			
		}catch(CSSubsystemException e){
			String msg = "Communication subsystem error while creating the client's facade";
			log.error(msg);
			e.printStackTrace();
		} catch (IOException e) {
			String msg = "Input/Output error while creating the client's facade";
			log.error(msg);
			e.printStackTrace();
		}
		
		log.debug("WRM: Client facade created");
		facade.start();
		log.debug("WRM: the facade is listening at port: "+facade.getPort());
		log.debug("WRM: the facade is running "+facade.isRunning());
		try {
		    resolver.setLocalListeningAddress(InetAddress.getByName(facade.getHost()), facade.getPort());
		    log.debug("WRM: Local address set into the resolver");
		} catch (UnknownHostException e) {
		    log.error("Unknown host exception",e);
		    throw new TazioRuntimeException(e);
		}
		
	}

	/* (non-Javadoc)
	 * @see org.mentalsmash.tazio.transparency.Transparency#disconnect()
	 */
	@Override
	public void disconnect() {
		
		facade.stop();
		throw new RuntimeException("Disconnection not implemented yet!");
		
	}

	/* (non-Javadoc)
	 * @see org.mentalsmash.tazio.transparency.Transparency#send(java.io.Serializable)
	 */
	@Override
	public <T extends Serializable> Serializable send(T message) throws TransparentDeliveryException {
		
		Serializable answer = null;
		
		TazioMessage toDeliver = messageFactory.encapsulateMessage(message);
		
		List<NID> recipients = resolver.getNIDList();
		
		Deliverer deliverer = new BaseDeliverer(resolver, toDeliver, recipients, MAX_SENDING_REPETITIONS);
		
		answer = deliverer.doDeliver().getPayload();
		
		return answer;
		
	}

	/* (non-Javadoc)
	 * @see org.mentalsmash.tazio.transparency.Transparency#sendAtMostOnce(java.io.Serializable)
	 */
	@Override
	public <T extends Serializable> Serializable sendAtMostOnce(T message)
			throws TransparentDeliveryException {
		
		Serializable answer = null;
		
		TazioMessage toDeliver = messageFactory.encapsulateMessage(message);
		
		List<NID> recipients = new ArrayList<NID>();
		recipients.add(resolver.getNIDList().get(0));
		
		Deliverer deliverer = new BaseDeliverer(resolver, toDeliver, recipients, MAX_SENDING_REPETITIONS);
		
		answer = deliverer.doDeliver().getPayload();
		
		return answer;
		
	}

	/* (non-Javadoc)
	 * @see org.mentalsmash.tazio.transparency.interfaces.Transparency#register(java.lang.Class, org.mentalsmash.tazio.transparency.interfaces.InputMessageListener)
	 */
	@Override
	public void register(Class<? extends Serializable> msgClass,
			InputMessageListener listener) {
		
		log.debug("Registering "+listener+" as a listener for "+msgClass+".");
	
		dispatcher.register(msgClass, listener);
	}

	/* (non-Javadoc)
	 * @see org.mentalsmash.tazio.transparency.interfaces.Transparency#unregister(java.lang.Class, org.mentalsmash.tazio.transparency.interfaces.InputMessageListener)
	 */
	@Override
	public void unregister(Class<? extends Serializable> msgClass,
			InputMessageListener listener) {
		
		log.debug("Unregistering "+listener+" as a listener for "+msgClass+".");
		dispatcher.unregister(msgClass, listener);
		
	}

}
